﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Reflection.Emit;
using System.Reflection;
using System.Collections.Generic;
using System.Xml.Linq;
using CommunitySystem;

namespace EHR.Ctrls.QueryDesign
{
    public static class DataModelBuilder
    {
        #region 静态字段
        private static AssemblyBuilder _assemblyBuilder;
        private static ModuleBuilder _moduleBuilder;
        private static IDictionary<string, Type> _dicTypes = new Dictionary<string, Type>();
        #endregion

        #region XML to List
        public static IList<object> BuildObjectList(ViewDesignInfo dataModelTypeInfo, XElement xData)
        {
            BuildTempAssembly();
            Type t = BuildDataModelType(dataModelTypeInfo);
            return BuildList(t, xData);
        }

        private static IList<object> BuildList(Type t, XElement xData)
        {
            IList<object> list = new List<object>();
            var xRows = xData.Element("Rows").Elements();
            var xCols = xData.Element("Columns").Elements();
            IDictionary<string, FieldType> fDic = new Dictionary<string, FieldType>();
            foreach (XElement xCol in xCols)
            {
                string fn = xCol.Attribute("ColumnName").Value;
                FieldType ft = (FieldType)int.Parse(xCol.Attribute("ColumnType").Value);
                fDic.Add(fn, ft);
            }

            foreach (XElement xRow in xRows)
            {
                object obj = Activator.CreateInstance(t);
                list.Add(obj);
                foreach (var p in fDic)
                {
                    string val = xRow.Attribute(p.Key).Value;
                    switch (p.Value)
                    {
                        case FieldType.String:
                            obj.SetProperty(p.Key, val);
                            break;
                        case FieldType.Int:
                            if (string.IsNullOrWhiteSpace(val))
                            {
                                obj.SetProperty(p.Key, null);
                            }
                            else
                            {
                                obj.SetProperty(p.Key, int.Parse(val));
                            }
                            break;
                        case FieldType.Float:
                            if (string.IsNullOrWhiteSpace(val))
                            {
                                obj.SetProperty(p.Key, null);
                            }
                            else
                            {
                                obj.SetProperty(p.Key, double.Parse(val));
                            }
                            break;
                        case FieldType.DateTime:
                            if (string.IsNullOrWhiteSpace(val))
                            {
                                obj.SetProperty(p.Key, null);
                            }
                            else
                            {
                                obj.SetProperty(p.Key, DateTime.Parse(val));
                            }
                            break;
                        case FieldType.Bool:
                            if (string.IsNullOrWhiteSpace(val))
                            {
                                obj.SetProperty(p.Key, null);
                            }
                            else
                            {
                                obj.SetProperty(p.Key, bool.Parse(val));
                            }
                            break;
                        default:
                            obj.SetProperty(p.Key, val);
                            break;
                    }
                }
            }

            return list;
        }
        #endregion

        #region 程序集、类型、构造方法
        public static void BuildTempAssembly()
        {
            if (_assemblyBuilder != null)
            {
                return;
            }
            _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("TempAssembly"), AssemblyBuilderAccess.Run);
            _moduleBuilder = _assemblyBuilder.DefineDynamicModule("TempModule");
        }

        public static Type BuildDataModelType(ViewDesignInfo v)
        {
            string typeName = v.TableName;
            if (_dicTypes.ContainsKey(typeName))
            {
                return _dicTypes[typeName];
            }

            TypeBuilder typeBuilder = CreateTypeBuilder(typeName);
            CreateConstructorBuilder(typeBuilder);
            //属性
            foreach (ColInfo col in v.Columns)
            {
                BuildProperty(typeBuilder, col.FieldName, col.FieldDotNetType);
            }
            Type t = typeBuilder.CreateType();
            _dicTypes.Add(typeName, t);
            return t;
        }

        private static TypeBuilder CreateTypeBuilder(string typeName)
        {
            TypeBuilder tb = _moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class, typeof(object));

            return tb;
        }

        private static ConstructorBuilder CreateConstructorBuilder(TypeBuilder typeBuilder)
        {
            return typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
        }
        #endregion

        #region 属性
        private static void BuildProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
        {
            //字段
            FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
            //属性
            PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
            //get方法
            BuildGetMethod(typeBuilder, propertyBuilder, fieldBuilder, propertyName, propertyType);
            //set方法
            BuildSetMethod(typeBuilder, propertyBuilder, fieldBuilder, propertyName, propertyType);
        }

        private static void BuildGetMethod(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, FieldBuilder fieldBuilder, string propertyName, Type propertyType)
        {
            MethodBuilder getMethodBuilder = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig
                , propertyType, Type.EmptyTypes);

            ILGenerator il = getMethodBuilder.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldfld, fieldBuilder);
            il.Emit(OpCodes.Ret);
            propertyBuilder.SetGetMethod(getMethodBuilder);
        }

        private static void BuildSetMethod(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, FieldBuilder fieldBuilder, string propertyName, Type propertyType)
        {
            MethodBuilder methodBuilder = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig
                , null, new Type[] { propertyType });

            ILGenerator il = methodBuilder.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Stfld, fieldBuilder);
            il.Emit(OpCodes.Ret);
            propertyBuilder.SetSetMethod(methodBuilder);
        }
        #endregion
    }
}
